home *** CD-ROM | disk | FTP | other *** search
/ MacHack 2001 / MacHack 2001.toast / pc / Sessions / Traut / ZStrings / Source / CrossPlatform / ZStringTool.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-06-23  |  26.5 KB  |  811 lines

  1. /*==================================================================
  2.     File:        ZStringTool.cpp
  3.     
  4.     Contains:    Tool-related code for building ZString override
  5.                 libraries, etc.
  6.  
  7.     Written by:    Eric Traut
  8.     
  9.     Copyright:    2000-2001 Connectix Corporation
  10.     
  11.     This source has been placed into the public domain by
  12.     Connectix Corporation. You have the right to modify, 
  13.     distribute or use this code without any legal limitations
  14.     or finanicial/licensing requirements. Connectix is not 
  15.     liable for any problems that result from the use of this 
  16.     code.
  17.     
  18.     If you have comments, feedback, questions, or would like
  19.     to submit bug fixes or updates to this code, please email
  20.     opensource@connectix.com.
  21. ==================================================================*/
  22.  
  23. #include "ZStringTool.h"
  24.  
  25. #include <stdio.h>
  26. #include <stdlib.h>
  27. #include <new>
  28.  
  29.  
  30. /******************************************************************
  31.  
  32.     EXPLANATION:
  33.     
  34.     This class can be used to create a tool that scans a binary
  35.     for named ZStrings. These strings should look like:
  36.         <Z name=/path1/path2/path3/...>string</Z>
  37.     In this example, pathN can be replaced with any strings
  38.     needed to describe the enclosed string. The value named
  39.     "string" in the above example should contain the actual
  40.     string prototype (including escape characters).
  41.  
  42.     The tool allows for the scanning of one or two binaries
  43.     (Note that the second "binary" may be a text file in practice.)
  44.     The first binary is the application that contains the ZStrings.
  45.     The second binary will generally be the last output file from
  46.     this tool. By scanning both files, the tool can determine 
  47.     whether any strings have been modified, added, or deleted
  48.     since the last time the tool was run.
  49.  
  50. ******************************************************************/
  51.  
  52.  
  53. /*------------------------------------------------------------------
  54.     ZStringTool
  55. ------------------------------------------------------------------*/
  56.  
  57. ZStringTool::ZStringTool()
  58. {
  59.     memset(mZStringToolHash, 0, sizeof(mZStringToolHash));
  60.     mStringRegisterCount = 0;
  61.     mStringLengthSum = 0;
  62.     mSortedList = NULL;
  63. }
  64.         
  65.  
  66. /*------------------------------------------------------------------
  67.     ~ZStringTool
  68. ------------------------------------------------------------------*/
  69.  
  70. ZStringTool::~ZStringTool()
  71. {
  72.     if (mSortedList != NULL)
  73.         delete[] mSortedList;
  74.     
  75.     for (Z_UInt32 hashTableIndex = 0; hashTableIndex < kZDictionaryHashEntries; hashTableIndex++)
  76.     {
  77.         ZToolEntry *    curEntry;
  78.         ZToolEntry *    nextEntry;
  79.         curEntry = mZStringToolHash[hashTableIndex];
  80.         
  81.         while (curEntry != NULL)
  82.         {
  83.             nextEntry = curEntry->mNext;
  84.             delete curEntry;
  85.             curEntry = nextEntry;
  86.         }
  87.     }
  88. }
  89.  
  90. /*------------------------------------------------------------------
  91.     LookUpString
  92. ------------------------------------------------------------------*/
  93.  
  94. ZToolEntry *
  95. ZStringTool::LookUpString(
  96.     const ZStringParseInfo &    inParseInfo)
  97. {
  98.     Z_UInt32            entryIndex = ZStringDictionary::HashString(inParseInfo.fNameStr, inParseInfo.fNameStrLen);
  99.     ZToolEntry *        curEntry = mZStringToolHash[entryIndex];
  100.     
  101.     while (curEntry != NULL)
  102.     {
  103.         // Do the lengths match?
  104.         if (curEntry->mParseInfo.fNameStrLen == inParseInfo.fNameStrLen)
  105.         {
  106.             Z_UInt32 curCharIndex;
  107.             Z_Boolean stringsMatch = true;
  108.  
  109.             for (curCharIndex = 0; curCharIndex < curEntry->mParseInfo.fNameStrLen; curCharIndex++)
  110.             {
  111.                 if (curEntry->mParseInfo.fNameStr[curCharIndex] != inParseInfo.fNameStr[curCharIndex])
  112.                 {
  113.                     stringsMatch = false;
  114.                     break;
  115.                 }
  116.             }
  117.             
  118.             // Did we find a match? If so, return the string.
  119.             if (stringsMatch)
  120.                 return curEntry;
  121.         }
  122.         
  123.         curEntry = curEntry->mNext;
  124.     }
  125.  
  126.     return NULL;
  127. }
  128.  
  129.  
  130. /*------------------------------------------------------------------
  131.     RegisterString
  132. ------------------------------------------------------------------*/
  133.  
  134. void
  135. ZStringTool::RegisterString(
  136.     const ZStringParseInfo &    inParseInfo,
  137.     ZStringToolState            inState)
  138. {
  139.     try
  140.     {
  141.         ZToolEntry *         newEntry = new ZToolEntry;
  142.         Z_UInt32            entryIndex = ZStringDictionary::HashString(inParseInfo.fNameStr, inParseInfo.fNameStrLen);
  143.  
  144.         newEntry->mParseInfo = inParseInfo;
  145.         newEntry->mStringState = inState;
  146.         
  147.         newEntry->mNext = mZStringToolHash[entryIndex];
  148.         mZStringToolHash[entryIndex] = newEntry;
  149.  
  150.         mStringRegisterCount++;
  151.         
  152.         // If the string was badly formed, it may not have a valid length.
  153.         if (inState != kStringBadlyFormed)
  154.             mStringLengthSum += inParseInfo.fNamedStringLimit - inParseInfo.fNamedStringStart;
  155.     }
  156.     catch (...)
  157.     {
  158.         fprintf(stderr, "ZStringTool ran out of memory. Couldn't allocate new object.");
  159.     }
  160. }
  161.  
  162.  
  163. /*------------------------------------------------------------------
  164.     ProcessBinaries
  165. ------------------------------------------------------------------*/
  166.  
  167. void
  168. ZStringTool::ProcessBinaries(
  169.     const char *                inNewStart,
  170.     Z_UInt32                    inNewLength,
  171.     const char *                inOldStart,
  172.     Z_UInt32                    inOldLength,
  173.     const ZToolOptions &        inOptions)
  174. {
  175.     mProcessingNew = true;
  176.     ProcessBinary(inNewStart, inNewLength, (inOldStart == NULL) ? 0 : kStringAdded, inOptions);
  177.  
  178.     if (inOldStart != NULL)
  179.     {
  180.         mProcessingNew = false;
  181.         ProcessBinary(inOldStart, inOldLength, 0, inOptions);
  182.     }
  183. }
  184.  
  185.  
  186. /*------------------------------------------------------------------
  187.     ProcessBinary
  188.     
  189.     This method processes an in-memory version of a binary
  190.     file. The caller must read it into a single, contiguous buffer.
  191. ------------------------------------------------------------------*/
  192.  
  193. void
  194. ZStringTool::ProcessBinary(
  195.     const void *                inStart,
  196.     Z_UInt32                    inLength,
  197.     ZStringToolState            inInitialState,
  198.     const ZToolOptions &        inOptions)
  199. {
  200.     const char *        curCharPtr = reinterpret_cast<const char *>(inStart);
  201.     const char *        limitCharPtr = curCharPtr + inLength;
  202.     ZStringParseInfo    parseInfo;
  203.     ZToolEntry *        existingEntry;
  204.     const char *        beginningTag = (inOptions.mHasOTags ? "<O name=" : "<Z name=");  // added to allow resource compares
  205.     
  206.     while (curCharPtr < limitCharPtr)
  207.     {
  208.         // Look for the start of a ZString.
  209.         if (curCharPtr[0] == '<' && strncmp(curCharPtr, beginningTag, 8) == 0)
  210.         {
  211.             if (!ZStringParser::ParseNamedString(curCharPtr, parseInfo, false))
  212.             {
  213.                 if (mProcessingNew)                        // Indicates a ZString missing </Z> 
  214.                     RegisterString(parseInfo, kStringBadlyFormed);
  215.             }
  216.             else
  217.             {
  218.                 ZString *                convertedForm;
  219.                 ZStringParseInfo        convertedParseInfo;
  220.                 ZParserWarningType        parserWarnings;
  221.                 
  222.                 convertedForm = new ZString;
  223.                 check(convertedForm != NULL);
  224.                 if (convertedForm == NULL)
  225.                 {
  226.                     printf("Ran out of memory");
  227.                     return;
  228.                 }
  229.                 
  230.                 // Convert the tags in the ZString to either all numeric or all alphabetic
  231.                 // Returns an error when there are incorrectly formed tags
  232.                 if (!ZStringParser::ConvertNamedStringToTag(parseInfo, inOptions, *convertedForm, parserWarnings))
  233.                 {
  234.                     if (mProcessingNew)                     
  235.                         RegisterString(parseInfo, kStringBadlyFormed);
  236.                     curCharPtr++;            // advance by one because a string missing </Z> will skip over the next zstring!
  237.                     continue;
  238.                 }
  239.  
  240.                 // Reparse the new converted form
  241.                 // Returns an error when there is an incorrect ZString tag form
  242.                 if (!ZStringParser::ParseNamedString(convertedForm->GetCString(), convertedParseInfo, false))
  243.                 {
  244.                     if (mProcessingNew)                     
  245.                         RegisterString(parseInfo, kStringBadlyFormed);  // send in the old parse info because the new one may not be set
  246.                     curCharPtr = parseInfo.fNamedStringLimit;  // Skip over the rest of the ZString
  247.                     continue;
  248.                 }
  249.  
  250.                 // Check for line breaks & flag this as a warning
  251.                 if (strpbrk(convertedForm->GetCString(), "\n\r") != NULL)
  252.                 {
  253.                     if (mProcessingNew)
  254.                         RegisterString(convertedParseInfo, kStringBreakWarning);  // send in new parse info because the previous function returned ok
  255.                     curCharPtr = parseInfo.fNamedStringLimit;  // Skip over the rest of the ZString
  256.                     continue;                        
  257.                 }
  258.                 
  259.                 // Check for high bit ASCII characters
  260.                 Z_Boolean    hasHiASCII = false;
  261.                 for (Z_UInt16 i = 0; i < convertedForm->GetLength(); i++)
  262.                 {
  263.                     if (static_cast<Z_UInt8>(convertedForm->GetCString()[i]) > 127)
  264.                         hasHiASCII = true;
  265.                 }
  266.  
  267.                 // If a limit tag exists, check the length of the data string (warnings are produced if over sized)
  268.                 Z_Boolean exceedsLimit = false;
  269.                 if (convertedParseInfo.fHasMaxDataLen)
  270.                     exceedsLimit = ZStringParser::CheckDataLength(convertedParseInfo);
  271.                 
  272.                 // Is there already an entry?
  273.                 existingEntry = LookUpString(convertedParseInfo);
  274.                 
  275.                 if (mProcessingNew)
  276.                 {
  277.                     // Derive state
  278.                     ZStringToolState    state = inInitialState;
  279.                     
  280.                     // Indicate any warnings
  281.                     if ((parserWarnings & kZParser_ChangedString) != 0)
  282.                         state |= kStringChangeWarning; 
  283.                     if ((parserWarnings & kZParser_FoundPossibleTag) != 0)
  284.                         state |= kStringPossibleTagWarning;
  285.                     if (exceedsLimit)
  286.                         state |= kStringLimitWarning; 
  287.  
  288.                     if (existingEntry != NULL)    // If we already encountred this string, it's a duplicate.
  289.                         state |= kStringDuplicate;
  290.                     if (hasHiASCII || (parserWarnings & kZParser_HasHighASCII) != 0)
  291.                         state |= kStringHasHighASCII;
  292.                     
  293.                     if ( existingEntry == NULL 
  294.                         || strcmp(convertedForm->GetCString(), existingEntry->mParseInfo.fValueStr) == 0 )    // ignore identical duplicates
  295.                         RegisterString(convertedParseInfo, state);
  296.                     else if (inOptions.mFlagDuplicates)                    // Indicate that this is a duplicate
  297.                         existingEntry->mStringState |= kStringDuplicate; 
  298.                 }
  299.                 else
  300.                 {
  301.                     // If there's an existing string, we need to determine
  302.                     // whether it's the same as the new binary string.
  303.                     if (existingEntry != NULL)
  304.                     {
  305.                         // If the string is the same, mark the string as not being added.
  306.                         if (NamedStringsMatch(existingEntry->mParseInfo, convertedParseInfo))
  307.                         {
  308.                             existingEntry->mStringState &= ~kStringAdded;
  309.                         }
  310.                         else
  311.                         {
  312.                             existingEntry->mStringState &= ~kStringAdded;
  313.                             existingEntry->mStringState |= kStringModifiedNew;
  314.                             
  315.                             // Derive state
  316.                             ZStringToolState    state = kStringModifiedOld;
  317.  
  318.                             // Indicate any warnings
  319.                             if ((parserWarnings & kZParser_ChangedString) != 0)
  320.                                 state |= kStringChangeWarning;
  321.                             if ((parserWarnings & kZParser_FoundPossibleTag) != 0)
  322.                                 state |= kStringPossibleTagWarning;
  323.                             if (exceedsLimit)
  324.                                 state |= kStringLimitWarning;
  325.                             if (hasHiASCII || (parserWarnings & kZParser_HasHighASCII) != 0)
  326.                                 state |= kStringHasHighASCII;
  327.  
  328.                             RegisterString(convertedParseInfo, state);
  329.                         }
  330.                     }
  331.                     else
  332.                     {
  333.                         // We didn't find the string in the new binary, so
  334.                         // we'll assume it was deleted.
  335.                         RegisterString(convertedParseInfo, kStringDeleted);
  336.                     }
  337.                     
  338.                 }
  339.             
  340.                 curCharPtr = parseInfo.fNamedStringLimit - 1;
  341.             }
  342.         }
  343.         
  344.         curCharPtr++;
  345.     }
  346. }
  347.  
  348.  
  349. /*------------------------------------------------------------------
  350.     NamedStringsMatch
  351. ------------------------------------------------------------------*/
  352.  
  353. Z_Boolean
  354. ZStringTool::NamedStringsMatch(
  355.     const ZStringParseInfo &    inString1,
  356.     const ZStringParseInfo &    inString2)
  357. {
  358.     check(inString1.fValidNamedString && inString2.fValidNamedString);
  359.     
  360.     // If one or the other is not valid, the strings are not equivalent.
  361.     if (!inString1.fValidNamedString || !inString2.fValidNamedString)
  362.         return false;
  363.  
  364.     // If the strings are different lengths, they differ.
  365.     if (inString1.fValueStrLen != inString2.fValueStrLen)
  366.         return false;
  367.     
  368.     for (Z_UInt16 charIndex = 0; charIndex < inString1.fValueStrLen; charIndex++)
  369.     {
  370.         if (inString1.fValueStr[charIndex] != inString2.fValueStr[charIndex])
  371.             return false;
  372.     }
  373.     
  374.     return true;
  375. }
  376.  
  377.  
  378. /*------------------------------------------------------------------
  379.     CreateSortedStringList
  380. ------------------------------------------------------------------*/
  381.  
  382. Z_Boolean
  383. ZStringTool::CreateSortedStringList(
  384.     Z_Boolean                    inCategorizeOutput)
  385. {
  386.     mSortedList = new (std::nothrow) ZToolEntry *[mStringRegisterCount];
  387.     if (mSortedList == NULL)
  388.     {
  389.         fprintf(stderr, "Out of memory. Couldn't allocate sorted string list.");
  390.         return false;
  391.     }
  392.     
  393.     Z_UInt32            sortedListIndex = 0;
  394.     
  395.     // Copy all the entry pointers from the linked lists to the new list.
  396.     for (Z_UInt32 hashTableIndex = 0; hashTableIndex < kZDictionaryHashEntries; hashTableIndex++)
  397.     {
  398.         ZToolEntry *            curHashListPtr;
  399.         
  400.         curHashListPtr = mZStringToolHash[hashTableIndex];
  401.         while (curHashListPtr != NULL)
  402.         {
  403.             check(sortedListIndex < mStringRegisterCount);
  404.             mSortedList[sortedListIndex++] = curHashListPtr;
  405.             
  406.             curHashListPtr = curHashListPtr->mNext;
  407.         }
  408.     }
  409.     
  410.     check(sortedListIndex == mStringRegisterCount);
  411.     
  412.     // Now, sort the list using qsort, either by category or only alphabetically
  413.     if (inCategorizeOutput)                
  414.         qsort(mSortedList, mStringRegisterCount, sizeof(ZToolEntry *), CompareCategoryFunction);
  415.     else
  416.         qsort(mSortedList, mStringRegisterCount, sizeof(ZToolEntry *), CompareStringFunction);
  417.     return true;
  418. }
  419.  
  420.  
  421. /*------------------------------------------------------------------
  422.     CompareToolEntry
  423.  
  424.     This method decides if the first string is greater than, less than
  425.     or the same as the second string, based upon the state of the 
  426.     strings and the string contents.
  427. ------------------------------------------------------------------*/
  428.  
  429. int
  430. ZStringTool::CompareToolEntries(
  431.     Z_UInt16                    inState,
  432.     const void *                inNode1,
  433.     const void *                inNode2)
  434. {
  435.     const ZToolEntry *        zString1 = *(const ZToolEntry **)(inNode1);
  436.     const ZToolEntry *        zString2 = *(const ZToolEntry **)(inNode2);
  437.     Z_Boolean                twoIsGreater = false;
  438.  
  439.     // Depending on the given state, decide whether the second string 
  440.     // would be greater than the first string
  441.     
  442.     switch (inState)
  443.     {
  444.         case kStringDeleted:
  445.             twoIsGreater = ((zString2->mStringState & (kStringAdded | kStringModifiedNew | kStringModifiedOld)) != 0); 
  446.             break;
  447.             
  448.         case kStringDuplicate:
  449.             twoIsGreater = ((zString2->mStringState & (kStringAdded | kStringModifiedNew | kStringModifiedOld | kStringDeleted)) != 0); 
  450.             break; 
  451.             
  452.         case kStringBadlyFormed:
  453.             twoIsGreater = ((zString2->mStringState & (kStringAdded | kStringModifiedNew | kStringModifiedOld | kStringDeleted | kStringDuplicate)) != 0); 
  454.             break; 
  455.             
  456.         case kStringBreakWarning:
  457.             twoIsGreater = ((zString2->mStringState & (kStringAdded | kStringModifiedNew | kStringModifiedOld | kStringDeleted | kStringDuplicate | kStringBadlyFormed)) != 0); 
  458.             break; 
  459.             
  460.         case kStringPossibleTagWarning:
  461.             twoIsGreater = ((zString2->mStringState & (kStringAdded | kStringModifiedNew | kStringModifiedOld | kStringDeleted | kStringDuplicate | kStringBadlyFormed | kStringBreakWarning)) != 0); 
  462.             break; 
  463.             
  464.         case kStringLimitWarning:
  465.             twoIsGreater = ((zString2->mStringState & (kStringAdded | kStringModifiedNew | kStringModifiedOld | kStringDeleted | kStringDuplicate | kStringBadlyFormed | kStringBreakWarning | kStringPossibleTagWarning)) != 0); 
  466.             break; 
  467.             
  468.         case kStringChangeWarning:
  469.             twoIsGreater = ((zString2->mStringState & (kStringAdded | kStringModifiedNew | kStringModifiedOld | kStringDeleted | kStringDuplicate | kStringBadlyFormed | kStringBreakWarning | kStringPossibleTagWarning | kStringLimitWarning)) != 0); 
  470.             break; 
  471.             
  472.         case kStringHasHighASCII:
  473.             twoIsGreater = ((zString2->mStringState & (kStringAdded | kStringModifiedNew | kStringModifiedOld | kStringDeleted | kStringDuplicate | kStringBadlyFormed | kStringBreakWarning | kStringPossibleTagWarning | kStringLimitWarning | kStringChangeWarning)) != 0); 
  474.             break; 
  475.     }
  476.  
  477.     if ((zString2->mStringState & inState) != 0)            // If the states are the same
  478.         return CompareStringFunction(inNode1, inNode2);     // order alphabetically
  479.     else if (twoIsGreater)
  480.         return 1;
  481.     else return -1;
  482. }
  483.  
  484.  
  485. /*------------------------------------------------------------------
  486.     CompareCategoryFunction
  487.  
  488.     This method sorts the output based upon the types of messages.
  489.     Messages are grouped in the following way:
  490.         Added
  491.         Modified New / Modified Old pairs
  492.         Deleted
  493.         Duplicates
  494.         Badly Formed
  495.         Line Break Warnings
  496.         String contains a possible tag warning
  497.         String Limit Exceeded Warnings
  498.         String character changed warnings (when converted high ascii into tags)
  499.         Has High ASCII
  500. ------------------------------------------------------------------*/
  501.  
  502. int
  503. ZStringTool::CompareCategoryFunction(
  504.     const void *                inNode1,
  505.     const void *                inNode2)
  506. {
  507.     const ZToolEntry *        zString1 = *(const ZToolEntry **)(inNode1);
  508.     const ZToolEntry *        zString2 = *(const ZToolEntry **)(inNode2);
  509.     
  510.     // Sort the entries based on the state of the first and second strings
  511.  
  512.     if ((zString1->mStringState & kStringAdded) != 0) 
  513.     {
  514.         if ((zString2->mStringState & kStringAdded) != 0)
  515.             return CompareStringFunction(inNode1, inNode2);
  516.         else return -1;
  517.     }
  518.     else if ((zString1->mStringState & kStringModifiedNew) != 0 || (zString1->mStringState & kStringModifiedOld) != 0)
  519.     {
  520.         if ((zString2->mStringState & kStringModifiedNew) != 0 || (zString2->mStringState & kStringModifiedOld) != 0)
  521.         {
  522.             if (!zString1->mParseInfo.fValidNamedString || !zString2->mParseInfo.fValidNamedString)
  523.                 return 0;
  524.             int order = strncmp(zString1->mParseInfo.fNameStr, zString2->mParseInfo.fNameStr, zString1->mParseInfo.fNameStrLen);
  525.             if (order == 0)
  526.             {
  527.                 if ((zString1->mStringState & kStringModifiedNew) != 0 && (zString2->mStringState & kStringModifiedOld) != 0)
  528.                     return -1;
  529.                 else if ((zString1->mStringState & kStringModifiedOld) != 0 && (zString2->mStringState & kStringModifiedNew) != 0)
  530.                     return 1;
  531.                 else return 0;
  532.             }
  533.             else return order;
  534.         }
  535.         else if ((zString2->mStringState & kStringAdded) != 0)
  536.             return 1;
  537.         else return -1;
  538.     }
  539.     else if ((zString1->mStringState & kStringDeleted) != 0) 
  540.         return CompareToolEntries(kStringDeleted, inNode1, inNode2);
  541.  
  542.     else if ((zString1->mStringState & kStringDuplicate) != 0) 
  543.         return CompareToolEntries(kStringDuplicate, inNode1, inNode2);
  544.  
  545.     else if ((zString1->mStringState & kStringBadlyFormed) != 0) 
  546.         return CompareToolEntries(kStringBadlyFormed, inNode1, inNode2);
  547.  
  548.     else if ((zString1->mStringState & kStringBreakWarning) != 0) 
  549.         return CompareToolEntries(kStringBreakWarning, inNode1, inNode2);
  550.  
  551.     else if ((zString1->mStringState & kStringPossibleTagWarning) != 0) 
  552.         return CompareToolEntries(kStringPossibleTagWarning, inNode1, inNode2);
  553.  
  554.     else if ((zString1->mStringState & kStringLimitWarning) != 0) 
  555.         return CompareToolEntries(kStringLimitWarning, inNode1, inNode2);
  556.  
  557.     else if ((zString1->mStringState & kStringChangeWarning) != 0) 
  558.         return CompareToolEntries(kStringChangeWarning, inNode1, inNode2);
  559.  
  560.     else if ((zString1->mStringState & kStringHasHighASCII) != 0)
  561.         return CompareToolEntries(kStringHasHighASCII, inNode1, inNode2);
  562.  
  563.     else if (zString2->mStringState != kStringUnique)    // First string doesn't have a problem but second string does 
  564.         return 1;
  565.  
  566.     else
  567.         return CompareStringFunction(inNode1, inNode2);
  568. }
  569.  
  570.  
  571. /*------------------------------------------------------------------
  572.     CompareStringFunction
  573. ------------------------------------------------------------------*/
  574.  
  575. int
  576. ZStringTool::CompareStringFunction(
  577.     const void *                inNode1,
  578.     const void *                inNode2)
  579. {
  580.     const ZToolEntry *        zString1 = *(const ZToolEntry **)(inNode1);
  581.     const ZToolEntry *        zString2 = *(const ZToolEntry **)(inNode2);
  582.     
  583.     // If the named strings aren't valid, we can't compare them.
  584.     if (!zString1->mParseInfo.fValidNamedString)
  585.         return -1;
  586.     else if (!zString2->mParseInfo.fValidNamedString)
  587.         return 1;
  588.     
  589.     return strncmp(zString1->mParseInfo.fNameStr, zString2->mParseInfo.fNameStr, zString1->mParseInfo.fNameStrLen);
  590. }
  591.  
  592.  
  593. /*------------------------------------------------------------------
  594.     PrintEntry
  595. ------------------------------------------------------------------*/
  596.  
  597. void
  598. ZStringTool::PrintEntry(
  599.     ZToolEntry &                inEntry,
  600.     FILE *                    inFile,
  601.     Z_Boolean                    inPrintWarnings)
  602. {
  603.     ZString        tempString;
  604.     
  605.     // Print status information first
  606.     // In order for the sorting by category to show up correctly, this order must be the same as the order in the sort category method
  607.     if ((inEntry.mStringState & kStringAdded) != 0)
  608.         fprintf(inFile, "<font color=\"#0000FF\"><font size=\"4\"><b>String Added</b></font><br>\n");
  609.     else if ((inEntry.mStringState & kStringModifiedNew) != 0)
  610.         fprintf(inFile, "<font color=\"#660066\"><font size=\"4\"><b>String Modified (New Version)</b></font><br>\n");
  611.     else if ((inEntry.mStringState & kStringModifiedOld) != 0)
  612.         fprintf(inFile, "<font color=\"#003333\"><font size=\"4\"><b>String Modified (Obsolete Version)</b></font><br>\n");
  613.     else if ((inEntry.mStringState & kStringDeleted) != 0)
  614.         fprintf(inFile, "<font color=\"#006600\"><font size=\"4\"><b>String Deleted</b></font><br>\n");
  615.     else if ((inEntry.mStringState & kStringDuplicate) != 0)
  616.         fprintf(inFile, "<font color=\"#009900\"><font size=\"4\"><b>Duplicate named string: Please rename one of them</b></font><br>\n");
  617.     else if ((inEntry.mStringState & kStringBadlyFormed) != 0)
  618.         fprintf(inFile, "<font color=\"#FF0000\"><font size=\"4\"><b>Badly formed string: Please fix</b></font><br>\n");
  619.     else if ((inEntry.mStringState & kStringBreakWarning) != 0 && inPrintWarnings)
  620.         fprintf(inFile, "<font color=\"#FF0099\"><font size=\"4\"><b>Warning: String contains line breaks</b></font><br>\n");
  621.     else if ((inEntry.mStringState & kStringPossibleTagWarning) != 0 && inPrintWarnings)
  622.         fprintf(inFile, "<font color=\"#FF0099\"><font size=\"4\"><b>Warning: String may contain malformed tags</b></font><br>\n");
  623.     else if ((inEntry.mStringState & kStringLimitWarning) != 0 && inPrintWarnings)
  624.         fprintf(inFile, "<font color=\"#FF0099\"><font size=\"4\"><b>Warning: String exceeds set limit</b></font><br>\n");
  625.     else if ((inEntry.mStringState & kStringChangeWarning) != 0 && inPrintWarnings)
  626.         fprintf(inFile, "<font color=\"#FF0099\"><font size=\"4\"><b>Warning: String contained high ASCII characters that have been converted to tags</b></font><br>\n");
  627.     else if ((inEntry.mStringState & kStringHasHighASCII) != 0 && inPrintWarnings)
  628.         fprintf(inFile, "<font color=\"#FF0099\"><font size=\"4\"><b>Warning: String contains High ASCII characters.</b></font><br>\n");
  629.     
  630.     // Now, print the named string.
  631.     if ((inEntry.mStringState & kStringBadlyFormed) != 0)
  632.     {
  633.         // If the string is badly formed, make sure that it has a valid length.  Otherwise, 256 is used.
  634.         Z_UInt16    outputLength = 256;
  635.         if (inEntry.mParseInfo.fNamedStringLimit > inEntry.mParseInfo.fNamedStringStart)
  636.             outputLength = inEntry.mParseInfo.fNamedStringLimit - inEntry.mParseInfo.fNamedStringStart;
  637.         tempString.SetString(inEntry.mParseInfo.fNamedStringStart, outputLength);
  638.     }
  639.     else
  640.     {
  641.         check(inEntry.mParseInfo.fNamedStringLimit > inEntry.mParseInfo.fNamedStringStart);
  642.         tempString.SetString(inEntry.mParseInfo.fNamedStringStart, inEntry.mParseInfo.fNamedStringLimit - inEntry.mParseInfo.fNamedStringStart);
  643.     }
  644.  
  645.     fprintf(inFile, "%s", tempString.GetCString());
  646.  
  647.     // Print the </font> tag if it is an error or it is a warning and warnings are to be printed
  648.     if ((inEntry.mStringState & kAllStringErrors) != 0 ||
  649.         ((inEntry.mStringState & kAllStringWarnings) != 0  && inPrintWarnings))
  650.         fprintf(inFile, "</font>\n");
  651.     
  652.     fprintf(inFile, "<br><br>\n");
  653. }
  654.  
  655.  
  656. /*------------------------------------------------------------------
  657.     CreateOverrideDictionary
  658. ------------------------------------------------------------------*/
  659.  
  660. Z_Boolean
  661. ZStringTool::CreateOverrideDictionary(
  662.     FILE *                    inFile)
  663. {
  664.     char *            dictionary;
  665.     Z_UInt32        dictLength;
  666.     Z_Boolean        success;
  667.     
  668.     success = CreateOverrideDictionary(dictionary, dictLength);
  669.     if (!success)
  670.         return success;
  671.     
  672.     check(dictionary != NULL); 
  673.     
  674.     if (fwrite(dictionary, 1, dictLength, inFile) != dictLength)
  675.     {
  676.         fprintf(stderr, "File error encountered when writing file.");
  677.         return false;
  678.     }
  679.     
  680.     delete [] dictionary;
  681.     
  682.     return true;
  683. }
  684.  
  685.  
  686. /*------------------------------------------------------------------
  687.     AddEntryToOverrideDictionary
  688. ------------------------------------------------------------------*/
  689.  
  690. char *
  691. ZStringTool::AddEntryToOverrideDictionary(
  692.     char *                        inOutputPtr,
  693.     const ZToolEntry &            inEntry)
  694. {
  695.     Z_UInt32        stringLength;
  696.     
  697.     if (!inEntry.mParseInfo.fValidNamedString)
  698.     {
  699.         fprintf(stderr, "Invalid string found. Dictionary may be invalid.\n");
  700.     }
  701.     else
  702.     {
  703.         char *string;
  704.  
  705.         stringLength = inEntry.mParseInfo.fNamedStringLimit - inEntry.mParseInfo.fNamedStringStart;
  706.         memcpy(inOutputPtr, inEntry.mParseInfo.fNamedStringStart, stringLength);
  707.  
  708.         // Find <Z name= and replace with <O name=
  709.         string = strstr(inOutputPtr, "<Z name=");
  710.         if (string != NULL)
  711.             string[1] = 'O';
  712.  
  713.         // Find </Z> and replace with </O>
  714.         string = strstr(inOutputPtr, "</Z>");
  715.         if (string != NULL)
  716.             string[2] = 'O';
  717.  
  718.         inOutputPtr += stringLength;
  719.         
  720.         // Terminate the string
  721.         *inOutputPtr++ = '\0';
  722.     }
  723.     
  724.     return inOutputPtr;
  725. }
  726.  
  727.  
  728. /*------------------------------------------------------------------
  729.     CreateOverrideDictionary
  730. ------------------------------------------------------------------*/
  731.  
  732. Z_Boolean
  733. ZStringTool::CreateOverrideDictionary(
  734.     char * &                    outDictionary,
  735.     Z_UInt32 &                    outLength)
  736. {
  737.     outLength = mStringRegisterCount + mStringLengthSum + 1;
  738.     outDictionary = new (std::nothrow) char[outLength];
  739.     
  740.     if (outDictionary == NULL)
  741.         return false;
  742.     
  743.     // Now, fill in the dictionary.
  744.     char * curOutput = reinterpret_cast<char *>(outDictionary);
  745.     for (Z_UInt32 hashTableIndex = 0; hashTableIndex < kZDictionaryHashEntries; hashTableIndex++)
  746.     {
  747.         ZToolEntry *            curHashListPtr;
  748.         
  749.         curHashListPtr = mZStringToolHash[hashTableIndex];
  750.         while (curHashListPtr != NULL)
  751.         {
  752.             curOutput = AddEntryToOverrideDictionary(curOutput, *curHashListPtr);
  753.             curHashListPtr = curHashListPtr->mNext;
  754.         }
  755.     }
  756.     
  757.     // Fill in the final byte to indicate we're done.
  758.     *curOutput++ = '\0';
  759.     
  760.     // Make sure we got the correct length.
  761.     check(curOutput == (char *)outDictionary + outLength);
  762.     
  763.     return true;
  764. }
  765.  
  766.  
  767. /*------------------------------------------------------------------
  768.     PrintReport
  769.     
  770.     Returns true if everything is OK.
  771. ------------------------------------------------------------------*/
  772.  
  773. Z_Boolean
  774. ZStringTool::PrintReport(
  775.     FILE *                    inFile,
  776.     const ZToolOptions &        inOptions)
  777. {
  778.     Z_Boolean        foundError = false;
  779.     Z_Boolean        hasOnlyWarnings;            
  780.     
  781.     if (!CreateSortedStringList(inOptions.mCategorizeOutput))
  782.         return false;
  783.     
  784.     for (Z_UInt32 sortedListIndex = 0; sortedListIndex < mStringRegisterCount; sortedListIndex++)
  785.     {
  786.         hasOnlyWarnings = ((mSortedList[sortedListIndex]->mStringState & kAllStringWarnings) != 0);
  787.  
  788.         if (mSortedList[sortedListIndex]->mStringState != kStringUnique)        // if zstring generated errors or warnings
  789.             if (inOptions.mOutputWarnings || !hasOnlyWarnings)                    // Only found an error if we are printing warnings
  790.                 foundError = true;
  791.  
  792.         if (inOptions.mPrintErrorsOnly)            // if only printing errors
  793.         {
  794.             if (mSortedList[sortedListIndex]->mStringState != kStringUnique)    // only print those that are errors or warnings
  795.                 PrintEntry(*mSortedList[sortedListIndex], inFile, inOptions.mOutputWarnings);
  796.         }
  797.         else
  798.             PrintEntry(*mSortedList[sortedListIndex], inFile, inOptions.mOutputWarnings);
  799.     }
  800.     
  801.     if (foundError)
  802.     {
  803.         fprintf(stderr, "One or more warnings dumped into output. \n");
  804.         fprintf(stderr, "View output in HTML browser to see warnings.\n");
  805.     }
  806.     
  807.     return !foundError;
  808. }
  809.  
  810.  
  811.